﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.TextMining.Collapse
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class TableFieldValueDistributionOutput:ResultBase
    {
        #region props
        private string _TableName;
        /// <summary></summary>
        public string TableName
        {
            get
            {
                return this._TableName;
            }
            set
            {
                this._TableName = value;
            }
        }

        private string _FieldName;
        /// <summary></summary>
        public string FieldName
        {
            get
            {
                return this._FieldName;
            }
            set
            {
                this._FieldName = value;
            }
        }

        private SqlDbType _FieldDbType;
        /// <summary></summary>
        public SqlDbType FieldDbType
        {
            get
            {
                return this._FieldDbType;
            }
            set
            {
                this._FieldDbType = value;
            }
        }

        private Dictionary<string,int> _Spreads;
        /// <summary></summary>
        public Dictionary<string,int> Spreads
        {
            get
            {
                return this._Spreads;
            }
            set
            {
                this._Spreads = value;
            }
        }

        private List<ModePercentile> _ModePcts;
        /// <summary></summary>
        public List<ModePercentile> ModePcts
        {
            get
            {
                return this._ModePcts;
            }
            set
            {
                this._ModePcts = value;
            }
        }

        private double _EvennessIndex;
        /// <summary></summary>
        public double EvennessIndex
        {
            get
            {
                return this._EvennessIndex;
            }
            set
            {
                this._EvennessIndex = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public TableFieldValueDistributionOutput():base(Guid.Empty,Guid.Empty)
        {
            this._TableName = string.Empty;
            this._FieldName = string.Empty;
            this._FieldDbType = SqlDbType.VarChar;
            this._Spreads=new Dictionary<string, int>();
            this._ModePcts=new List<ModePercentile>();
            this._EvennessIndex = 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        /// <param name="tblName"></param>
        /// <param name="fieldName"></param>
        /// <param name="fieldDbType"></param>
        /// <param name="spreads"></param>
        /// <param name="modePcts"></param>
        /// <param name="evennessIdx"></param>
        public TableFieldValueDistributionOutput(
            Guid componentID, Guid jobTicketID,
            string tblName, string fieldName, SqlDbType fieldDbType,
            Dictionary<string,int> spreads, List<ModePercentile> modePcts,
            double evennessIdx) 
            : base(componentID, jobTicketID)
        {
            this._TableName = tblName;
            this._FieldName = fieldName;
            this._FieldDbType = fieldDbType;
            this._Spreads = spreads;
            this._ModePcts = modePcts;
            this._EvennessIndex = evennessIdx;
        }
        #endregion 

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xDoc"></param>
        /// <param name="parentNode"></param>
        /// <returns></returns>
        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Output");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "FinishTime", this.FinishTime.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"TableName",this.TableName);
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"FieldName",this.FieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"FieldDbType",((int)this.FieldDbType).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"EvennessIndex",this.EvennessIndex.ToString());

            if (this.Spreads != null && this.Spreads.Count > 0)
            {
                XmlNode spreadRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Spreads");
                foreach (string range in this.Spreads.Keys)
                {
                    string from = range.Substring(0, range.IndexOf("-"));
                    string to = range.Substring(range.IndexOf("-") + 1);
                    XmlNode rangeNode = XmlDataUtil.AddElement(ref xDoc, spreadRootNode, "Range");
                    XmlDataUtil.UpdateAttribute(ref xDoc, rangeNode, "From", from);
                    XmlDataUtil.UpdateAttribute(ref xDoc, rangeNode, "To", to);
                    XmlDataUtil.UpdateAttribute(ref xDoc, rangeNode, "Count", this.Spreads[range].ToString());
                }
            }

            if (this.ModePcts != null && this.ModePcts.Count > 0)
            {
                XmlNode modeRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "ModePercentiles");
                this.ModePcts.Sort();
                foreach (ModePercentile modePct in this.ModePcts)
                {
                    XmlNode modePctNode = XmlDataUtil.AddElement(ref xDoc, modeRootNode, "ModePercentile");
                    XmlDataUtil.UpdateAttribute(ref xDoc, modePctNode, "Percentile", modePct.Percentile.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, modePctNode, "Count", modePct.Count.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, modePctNode, "AvgFrequency", modePct.AverageFrequency.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, modePctNode, "SD", modePct.SD.ToString());

                    //foreach (string distinctValue in modePct.DistinctValues)
                    //{
                    //    XmlNode modeNode = XmlDataUtil.AddElement(ref xDoc, modePctNode, "Mode");
                    //    XmlDataUtil.UpdateAttribute(ref xDoc, modeNode, "Value", distinctValue);
                    //}
                }
            }

            return xNode;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xNode"></param>
        /// <returns></returns>
        public override ResultBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                TableFieldValueDistributionOutput output = new TableFieldValueDistributionOutput();
                output.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                output.FinishTime = DateTime.Parse(xNode.Attributes.GetNamedItem("FinishTime").Value);

                output.TableName = XmlDataUtil.GetAttributeValue(xNode, "TableName", "");
                output.FieldName = XmlDataUtil.GetAttributeValue(xNode, "FieldName", "");
                output.FieldDbType =
                    (SqlDbType)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "FieldDbType", ((int) SqlDbType.VarChar).ToString()));
                output.EvennessIndex = double.Parse(XmlDataUtil.GetAttributeValue(xNode, "EvennessIndex", "0"));

                output.Spreads=new Dictionary<string, int>();
                XmlNodeList rangeNodes = xNode.SelectNodes("Spreads/Range");
                if (rangeNodes != null && rangeNodes.Count > 0)
                {
                    if (FieldDataType.IsNumber(output.FieldDbType))
                    {
                        foreach (XmlNode rangeNode in rangeNodes)
                        {
                            int from = int.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "From", "0"));
                            int to = int.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "To", "0"));
                            int count = int.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "Count", "0"));
                            output.Spreads.Add(string.Format("{0}-{1}", from, to), count);
                        }
                    }
                    else if (FieldDataType.IsDateTime(output.FieldDbType))
                    {
                        foreach (XmlNode rangeNode in rangeNodes)
                        {
                            DateTime from = DateTime.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "From", "0"));
                            DateTime to = DateTime.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "To", "0"));
                            int count = int.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "Count", "0"));
                            output.Spreads.Add(string.Format("{0}-{1}", from, to), count);
                        }
                    }
                    else if (FieldDataType.IsStringType(output.FieldDbType))
                    {
                        foreach (XmlNode rangeNode in rangeNodes)
                        {
                            string from = XmlDataUtil.GetAttributeValue(rangeNode, "From", "0");
                            string to = XmlDataUtil.GetAttributeValue(rangeNode, "To", "0");
                            int count = int.Parse(XmlDataUtil.GetAttributeValue(rangeNode, "Count", "0"));
                            output.Spreads.Add(string.Format("{0}-{1}", from, to), count);
                        }
                    }
                }

                output.ModePcts = new List<ModePercentile>();
                XmlNodeList modePctNodes = xNode.SelectNodes("ModePercentiles/ModePercentile");
                if (modePctNodes != null && modePctNodes.Count > 0)
                {
                    foreach (XmlNode modePctNode in modePctNodes)
                    {
                        ModePercentile modePct = new ModePercentile();
                        modePct.AverageFrequency =
                            double.Parse(XmlDataUtil.GetAttributeValue(modePctNode, "AvgFrequency", "0"));
                        modePct.Count = int.Parse(XmlDataUtil.GetAttributeValue(modePctNode, "Count", "0"));
                        modePct.Percentile =
                            int.Parse(XmlDataUtil.GetAttributeValue(modePctNode, "Percentile", "0"));
                        modePct.SD = double.Parse(XmlDataUtil.GetAttributeValue(modePctNode, "SD", "0"));
                        modePct.DistinctValues = new List<string>();
                        //XmlNodeList modeNodes = modePctNode.SelectNodes("Mode");
                        //foreach (XmlNode modeNode in modeNodes)
                        //{
                        //    string distinctValue = XmlDataUtil.GetAttributeValue(modeNode, "Value", "");
                        //    if (!modePct.DistinctValues.Contains(distinctValue))
                        //    {
                        //        modePct.DistinctValues.Add(distinctValue);
                        //    }
                        //}
                        output.ModePcts.Add(modePct);
                    }
                }

                return output;
            }
            else
            {
                throw new Exception("Unable to instantiate output: invalid type specified in xml node");
            }
        }
    }
}
